#ifndef ENTITYFACTORY_H
#define ENTITYFACTORY_H

#include "Entity.h"
#include "Ship.h"
#include "Asteroid.h"
#include "Bullet.h"
#include "Bonus.h"
#include "PolygonFactory.h"
#include "Base.h"

namespace model
{
class EntityFactory
{
public:
	typedef enum BulletPolygonType
	{
		B_BULLET,
		B_LASER,
		B_MISSILE,
		B_BOMB,
		B_BOMB_DESTROYED
	};
	
	typedef enum BonusPolygonType
	{
		X_CIRCLE,
		X_RECTANGLE
	};

	~EntityFactory() {}

	static EntityFactory& instance()
	{
		static EntityFactory instance;
		return instance;
	}

	Ship* createShip(int id, std::string image, const Point &position, int rotationVelocity,  int maxVelocity, int propulsion, int friction, int bulletTimeOut)
	{
		Ship* ship = new Ship(id, image, position, rotationVelocity, maxVelocity, propulsion, friction, bulletTimeOut);
		ship->setPolygon(PolygonFactory::instance().createTriangle(position, Polygon::P_TopLeft, 48, 48));
		ship->initializeFeatures();

		return ship;
	}

	Base* createBase(int id, std::string image, const Point &position)
	{
		Base* base = new Base(id, image, position);
		base->setPolygon(PolygonFactory::instance().createCircle(position, Polygon::P_TopLeft, 150, 16));
		return base;
	}

	Asteroid* createAsteroid(int id, AsteroidType* type, const Point &position, double direction, double rotation, double velocity)
	{
		Asteroid* asteroid = new Asteroid(id, type, position, direction, rotation, velocity);
		
		if (type->getName().compare(ASTEROID_CIRCULO_NAME) == 0)
		{
			asteroid->setPolygon(PolygonFactory::instance().createCircle(position, Polygon::P_TopLeft, type->getWidth(), 16));
		}
		else if (type->getName().compare(ASTEROID_RECTANGULO_NAME) == 0)
		{
			 asteroid->setPolygon(PolygonFactory::instance().createRectangle(position, Polygon::P_TopLeft, type->getWidth(), type->getHeight()));
		}
		else if (type->getName().compare(ASTEROID_L_NAME) == 0)
		{
			asteroid->setPolygon(PolygonFactory::instance().createL(position, Polygon::P_TopLeft, type->getWidth(), type->getHeight()));
		}
		else if (type->getName().compare(ASTEROID_CUADRADO_NAME) == 0)
		{
			 asteroid->setPolygon(PolygonFactory::instance().createSquare(position, Polygon::P_TopLeft, type->getWidth()));
		}
		else
		{
			 asteroid->setPolygon(PolygonFactory::instance().createRectangle(position, Polygon::P_TopLeft));
		}
		return asteroid;
	}

	Bullet* createBullet(int id, Bullet::BulletType type, const Point& position, int duration, double startTime, BulletPolygonType polygonType=B_BULLET)
	{
		std::string image(DEFAULT_BULLET);
		if (type == Bullet::MISSILE)
		{
			image.clear();
			image.assign(DEFAULT_MISSILE);
			polygonType = B_MISSILE;
		}
		else if (type == Bullet::BOMB)
		{
			image.clear();
			image.assign(DEFAULT_BOMB);
			polygonType = B_BOMB;
		}

		else if (type == Bullet::BOMBEXPLOTED)
		{
			image.clear();
			image.assign(DEFAULT_BOMB_DESTROYED);
			polygonType = B_BOMB_DESTROYED;
		}
		else if (type == Bullet::LASER)
		{
			image.clear();
			image.assign(DEFAULT_LASER);
			polygonType = B_LASER;
		}

		if (!Utils::fileExists(image))
		{
			stringstream msg;
			msg << "No se encontro imagen DEFAULT de bullet de tipo " << Bullet::getTypeName(type) << ": '" << image << "'. Abortando ejecucion." << endl;
			Logger::instance().log(msg.str(), Logger::LINFO);
			throw "No se encontro imagen de bullet DEFAULT.";			
		}

		Bullet* bullet = new Bullet(id, image, position, duration, startTime);
		bullet->setType(type);
		switch (polygonType)
		{
		case B_BULLET:
			bullet->setPolygon(PolygonFactory::instance().createRectangle(position, Polygon::P_TopLeft, 4, 12));
			break;
		case B_LASER:
			bullet->setPolygon(PolygonFactory::instance().createRectangle(position, Polygon::P_TopLeft, 3, 40));
			break;
		case B_MISSILE:
			bullet->setPolygon(PolygonFactory::instance().createRectangle(position, Polygon::P_TopLeft, 9, 26));
			break;
		case B_BOMB:
			bullet->setPolygon(PolygonFactory::instance().createCircle(position, Polygon::P_TopLeft, 30, 8));
			break;
		case B_BOMB_DESTROYED:
			bullet->setPolygon(PolygonFactory::instance().createCircle(position, Polygon::P_TopLeft, 70, 16));
			break;
		default:
			bullet->setPolygon(PolygonFactory::instance().createRectangle(position, Polygon::P_TopLeft, 4, 12));
			break;
		}
		return bullet;
	}

	Bonus* createBonus(int id, const Point &position, Bonus::BonusType type, double startTime, BonusPolygonType polygonType=X_CIRCLE)
	{
		Bonus* bonus = new Bonus();
		bonus->setType(type);
		bonus->setId(id);
		bonus->setStartTime(startTime);
		bonus->setType(type);
		bonus->setX(position.x());
		bonus->setY(position.y());

		switch (polygonType)
		{
		case X_CIRCLE:
			bonus->setPolygon(PolygonFactory::instance().createCircle(position, Polygon::P_TopLeft, 30));
			break;
		case X_RECTANGLE:
			bonus->setPolygon(PolygonFactory::instance().createRectangle(position, Polygon::P_TopLeft, 30, 25));
			break;
		default:
			bonus->setPolygon(PolygonFactory::instance().createCircle(position, Polygon::P_TopLeft, 30));
			break;
		}
		return bonus;
	}
protected:
	EntityFactory() {}
};
};

#endif
